package com.ljx.recruitment.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljx.recruitment.common.constant.CacheConstants;
import com.ljx.recruitment.common.domain.vo.resp.PageResp;
import com.ljx.recruitment.common.enums.CommonStatusTypeEnum;
import com.ljx.recruitment.common.exception.ApiException;
import com.ljx.recruitment.common.util.RedisUtil;
import com.ljx.recruitment.interview.service.ResumeService;
import com.ljx.recruitment.security.common.UserContext;
import com.ljx.recruitment.security.util.UserUtil;
import com.ljx.recruitment.user.constant.IntegrationAvatarConstant;
import com.ljx.recruitment.user.domain.dto.UserDto;
import com.ljx.recruitment.user.domain.dto.UserFlagDto;
import com.ljx.recruitment.user.domain.entity.*;
import com.ljx.recruitment.user.domain.vo.req.*;
import com.ljx.recruitment.user.domain.vo.resp.LoginResp;
import com.ljx.recruitment.user.domain.vo.resp.UserResp;
import com.ljx.recruitment.user.domain.vo.resp.UserRoleResp;
import com.ljx.recruitment.user.enums.AuthTypeEnum;
import com.ljx.recruitment.user.enums.CommonRoleEnum;
import com.ljx.recruitment.user.enums.TenantMenuEnum;
import com.ljx.recruitment.user.mapper.UserMapper;
import com.ljx.recruitment.user.service.*;
import com.ljx.recruitment.user.service.impl.auth.AccountAuthServiceImpl;
import com.ljx.recruitment.user.service.impl.auth.EmailAuthServiceImpl;
import com.ljx.recruitment.user.service.impl.auth.PhoneAuthServiceImpl;
import com.ljx.recruitment.user.service.impl.auth.WxAuthServiceImpl;
import com.ljx.recruitment.user.typehandler.details.ResumeDetails;
import com.ljx.recruitment.user.util.EmailUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Author: LiJunXi
 * @Description:
 * @Date: 2024-10-12  15:24
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private TenantService tenantService;

    @Autowired
    private ResumeService resumeService;

    @Override
    public LoginResp login(AuthReq authReq) {
        AuthService authService = getAuthService(authReq);
        User user = authService.tryToLogin(authReq);
        Long tokenId = RandomUtils.nextLong();
        String token = UserUtil.createToken(user.getId(), tokenId);
        UserDto userDto = saveAndGetUserCache(user, tokenId);

        return LoginResp.builder()
                .token(token)
                .userInfo(userDto)
                .build();
    }

    private UserDto saveAndGetUserCache(User user, Long tokenId) {
        UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
        userDto.setPermissions(UserUtil.tryToGetUserPermission(user.getId()));
        userDto.setRoles(UserUtil.tryToGetUserRole(user.getId()));
        userDto.setMenus(UserUtil.tryToGetUserMenu(user.getId()));
        UserUtil.setTokenCache(userDto, tokenId);

        return userDto;
    }


    private AuthService getAuthService(AuthReq authReq) {
        if (AuthTypeEnum.ACCOUNT.getKey().equals(authReq.getAuthType())){
            return new AccountAuthServiceImpl();
        } else if (AuthTypeEnum.EMAIL.getKey().equals(authReq.getAuthType())){
            return new EmailAuthServiceImpl();
        } else if (AuthTypeEnum.PHONE.getKey().equals(authReq.getAuthType())){
            return new PhoneAuthServiceImpl();
        } else if (AuthTypeEnum.WX.getKey().equals(authReq.getAuthType())){
            return new WxAuthServiceImpl();
        } else {
            throw new ApiException("无效认证方式");
        }
    }

    @Override
    @Transactional
    public UserResp submitByUsername(UserSubmitReq req) {
        User user = getByUsernameAndTenant(req.getUsername(), UserUtil.tryToGetTenantId());
        if (Objects.isNull(user)) {
            user = BeanUtil.copyProperties(req, User.class);
            userMapper.insert(user);
            initUserRole(user);
        } else {
            // 忽略源对象属性为空的情况
            BeanUtil.copyProperties(req, user, CopyOptions.create()
                    .setIgnoreNullValue(true)
                    .setIgnoreError(true));
            userMapper.updateById(user);
        }
        return getInfo(user.getId());
    }

    private void initUserRole(User user) {
        Tenant tenant = tenantService.getById(user.getTenantId());
        String roleCode = CommonRoleEnum.getUserCode(tenant.getCode());
        Role role = roleService.getByName(roleCode);
        roleService.allocationToUser(new RoleToUserReq(user.getId(), Collections.singletonList(role.getId())));
    }

    @Override
    public UserResp getInfo(Long userId) {
        if (Objects.isNull(userId)){
            throw new ApiException("用户ID为空");
        }
        User user = userMapper.selectById(userId);
        return BeanUtil.copyProperties(user, UserResp.class);
    }

    @Override
    public User getByUsernameAndTenant(String username, Long tenantId) {
        return userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getUsername, username)
                .eq(User::getTenantId, tenantId)
                .eq(User::getStatus, CommonStatusTypeEnum.NORMAL.getKey()));
    }

    @Override
    @Transactional
    public UserResp register(AuthReq authReq) {
        AuthService authService = getAuthService(authReq);
        User user = authService.tryToRegister(authReq);
        saveUserRole(user.getId());

        return BeanUtil.copyProperties(user, UserResp.class);
    }

    private void saveUserRole(Long userId) {
        Tenant tenant = tenantService.getById(UserUtil.tryToGetTenantId());
        Role role = roleService.getByName(CommonRoleEnum.getUserCode(tenant.getCode()));
        userMapper.saveUserRole(userId, role.getId());
    }

    @Override
    public void checkPerAndRoleAndMenu(UserDto userDto) {
        UserDto user = UserContext.getUser();
        if (CollUtil.isNotEmpty(userDto.getPermissions()) && !CollUtil.isEqualList(user.getPermissions(), userDto.getPermissions())){
            throw new ApiException("权限信息发生变化，请重新登录");
        }
        if (CollUtil.isNotEmpty(userDto.getRoles()) && !CollUtil.isEqualList(user.getRoles(), userDto.getRoles())){
            throw new ApiException("角色信息发生变化，请重新登录");
        }
        if (CollUtil.isNotEmpty(userDto.getMenus()) && !CollUtil.isEqualList(user.getMenus(), userDto.getMenus())){
            throw new ApiException("菜单信息发生变化，请重新登录");
        }
    }

    @Override
    public PageResp<User> getPage(UserPageReq req, UserFlagDto userFlagDto) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (Objects.nonNull(userFlagDto.getIsSuperUser()) && userFlagDto.getIsSuperUser()){

        } else if (Objects.nonNull(userFlagDto.getIsTenantAdmin()) && userFlagDto.getIsTenantAdmin()){
            lambdaQueryWrapper.eq(User::getTenantId, UserUtil.tryToGetTenantId());
        } else {
            throw new ApiException("权限不足");
        }
        if (Objects.nonNull(req.getKey())){
            lambdaQueryWrapper.like(User::getNickname, req.getKey());
        }

        Page<User> page = userMapper.selectPage(new Page<>(req.getCurrPage(), req.getPageSize()), lambdaQueryWrapper);

        return PageResp.<User>builder()
                .list(page.getRecords())
                .total(page.getTotal())
                .build();
    }

    @Override
    public List<UserRoleResp> getUserRole(Long userId) {
        List<Long> roleIds = roleService.getUserRoleIdByUserId(userId);
        Map<Long, Long> roleMap = roleIds.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
        List<Role> tenantRoles = roleService.getRoleLikeName(tenantService.getById(UserUtil.tryToGetUserTenantId(userId)).getCode() + "_");

        return tenantRoles.stream().map(
                item -> new UserRoleResp(item.getId(), item.getName(), roleMap.containsKey(item.getId()))
        ).collect(Collectors.toList());
    }

    @Override
    public ResumeDetails submitUserResume(UserResumeReq req) {
        ResumeDetails resumeDetails = null;
        User user = userMapper.selectById(UserUtil.getUserId());
        if (req.getInit()) {
            resumeDetails = new ResumeDetails(resumeService.init());
        } else {
            resumeService.checkResumeParams(req.getResumeDetails());
            resumeDetails = req.getResumeDetails();
        }
        user.setResumeDetails(resumeDetails);
        userMapper.updateById(user);
        UserUtil.deleteUserCache(user.getId());
        return resumeDetails;
    }

    @Override
    public void logout(HttpServletRequest request) {
        String token = request.getHeader("token");
        Claims claims = UserUtil.getClaims(token);
        RedisUtil.del(CacheConstants.getKey(CacheConstants.LOGIN_TOKEN_KEY, claims.get("tokenId")));
    }

    @Override
    public UserResp submit(UserSubmitReq req) {
        checkSubmitParams(req);
        User user = userMapper.selectById(req.getId());
        updateUserInfo(req, user);
        UserUtil.deleteUserCache(user.getId());
        return getInfo(user.getId());
    }

    private void updateUserInfo(UserSubmitReq req, User user) {
        boolean needUpdateAvatar = IntegrationAvatarConstant.getAvatarByName(user.getNickname()).equals(req.getAvatar());
        boolean needUpdatePassword = StrUtil.isNotEmpty(req.getNewPassword());
        String oldPassword = user.getPassword();
        BeanUtils.copyProperties(req, user);
        if (needUpdateAvatar) {
            user.setAvatar(IntegrationAvatarConstant.getAvatarByName(user.getNickname()));
        }
        if (needUpdatePassword) {
            if (StrUtil.isNotEmpty(req.getOldPassword()) && !oldPassword.equals(DigestUtils.md5DigestAsHex(req.getOldPassword().getBytes()))) {
                throw new ApiException("原密码不正确");
            }
            user.setPassword(DigestUtils.md5DigestAsHex(req.getNewPassword().getBytes()));
        }
        userMapper.updateById(user);
    }


    private void checkSubmitParams(UserSubmitReq req){
        if (Objects.isNull(req.getId()) || (!req.getId().equals(UserUtil.getUserId()) && !UserUtil.checkIsTenantAdmin())) {
            throw new ApiException("身份验证出错");
        }
        if (StrUtil.isNotEmpty(req.getPhone()) && req.getPhone().length() != 11) {
            throw new ApiException("手机号格式不对");
        }
        if (StrUtil.isNotEmpty(req.getEmail())) {
            if (!EmailUtil.isValid(req.getEmail())) {
                throw new ApiException("邮箱格式不对");
            }
            User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, req.getPhone()));
            if (Objects.nonNull(user) && !UserUtil.checkIsTenantAdmin() && !user.getId().equals(UserUtil.getUserId())) {
                throw new ApiException("手机号已被使用");
            }
        }
    }

    @Override
    public User getByRecommendCode(String recommendCode) {
        if (StrUtil.isEmpty(recommendCode)) {
            throw new ApiException("内推码不能为空");
        }
        return userMapper.getByRecommendCode(recommendCode);
    }

    @Override
    public UserResp adminSaveUser(UserSubmitReq req) {
        checkAdminSubmitUserParams(req);
        User user = new User(req.getUsername(), DigestUtils.md5DigestAsHex(req.getPassword().getBytes()), UserUtil.tryToGetTenantId());
        user.setRecommendCode(RandomUtil.randomString(6));
        save(user);
        return BeanUtil.copyProperties(user, UserResp.class);
    }

    private void checkAdminSubmitUserParams(UserSubmitReq req) {
        if (StrUtil.isEmpty(req.getUsername()) || StrUtil.isEmpty(req.getPassword())) {
            throw new ApiException("重要参数不能为空");
        }
    }

    @Override
    public Integer getTenantUserCount(Long tenantId) {
        if (Objects.isNull(tenantId)) {
            throw new ApiException("租户ID为空");
        }
        return lambdaQuery()
                .eq(User::getTenantId, tenantId)
                .eq(User::getStatus, CommonStatusTypeEnum.NORMAL.getKey())
                .count();
    }
}
